import { createAsyncThunk, createSlice, isRejected } from "@reduxjs/toolkit";
import { postRequest } from "../util/Net";
import { RootState } from "./store";

// 1. Requests.

interface LoginRequest {
  userName: string;
  password: string;
}

interface RegisterRequest {
  userName: string;
  name: string;
  gender: number;
  password: string;
  inviteCode: string;
}

// 2. Responses.

interface LoginResponse {
  token: string;
  authorities: string[];
}

// 3. Params for reducers.

interface LoginParams {
  userName: string;
  password: string;
}

interface RegisterParams {
  userName: string;
  password: string;
  inviteCode: string;
}

// 4. State and initial value definition.

interface AuthState {
  loggedIn: boolean;
  token: string;
  authorities: string[];
  registered: boolean;
  errorOpen: boolean;
  errorMessage: string;
}

const initialState: AuthState = {
  loggedIn: false,
  token: '',
  registered: false,
  authorities: [],
  errorOpen: false,
  errorMessage: ''
};

// 5. Async functions definition.

const login = createAsyncThunk('api/login', async (params: LoginParams) => {
  const response = await postRequest<LoginResponse, LoginRequest>({
    url: '/account/login',
    request: {
      userName: params.userName,
      password: params.password
    },
    withToken: false
  });
  return response.data;
});

const register = createAsyncThunk('api/register', async (params: RegisterParams) => {

  // Send request, without token.
  const response = await postRequest<null, RegisterRequest>({
    url: '/account/register',
    request: {
      userName: params.userName,
      name: params.userName,
      password: params.password,
      gender: 1,
      inviteCode: params.inviteCode
    },
    withToken: false
  });

  // Return the json response of the net communication.
  return response.data;
});

// 6. Slice definition.

const authSlice = createSlice({
  name: 'auth',
  initialState: initialState,
  reducers: {
    clear: (state) => {
      state.loggedIn = false;
      state.token = '';
      state.authorities = [];

      localStorage.removeItem('token');
    },
    loadToken: (state) => {
      const token = localStorage.getItem('token');
      if (token != null) {
        state.token = token;
      }
    },
    closeError: (state) => {
      state.errorOpen = false;
    }
  },
  extraReducers: (builder) => {
    builder.addCase(login.fulfilled, (state, action) => {
      const data = action.payload.result;
      state.loggedIn = true;
      state.token = data.token;
      state.authorities = data.authorities;

      localStorage.setItem('token', data.token);
    }).addCase(register.fulfilled, (state, action) => {
      state.registered = true;
    }).addMatcher(isRejected, (state, action) => {
      if (action.error.message) {
        state.errorMessage = action.error.message;
        state.errorOpen = true;
      }
      console.log(action.error);
    });
  }
});

export default authSlice.reducer;
export const authActions = {...authSlice.actions, login, register};
export const selectLoggedIn = (root: RootState) => root.authReducer.loggedIn;
export const selectToken = (root: RootState) => root.authReducer.token;
export const selectAuthorities = (root: RootState) => root.authReducer.authorities;
export const selectErrorMessage = (root: RootState) => root.authReducer.errorMessage;
export const selectErrorOpen = (root: RootState) => root.authReducer.errorOpen;
export const selectRegistered = (root: RootState) => root.authReducer.registered;